package com.leizi.igym.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leizi.igym.BaseContext.UserThreadLocal;
import com.leizi.igym.constant.PlanConstant;
import com.leizi.igym.domain.Plan;
import com.leizi.igym.dto.PlanAddDTO;
import com.leizi.igym.dto.PlanDTO;
import com.leizi.igym.dto.PlanSearchDTO;
import com.leizi.igym.enums.ReturnCodeEnum;
import com.leizi.igym.exception.BusinessException;
import com.leizi.igym.mapper.PlanMapper;
import com.leizi.igym.pojo.ContextUser;
import com.leizi.igym.resp.Result;
import com.leizi.igym.service.PlanService;
import com.leizi.igym.utils.ThrowUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author PC
 * @description 针对表【t_plan】的数据库操作Service实现
 * @createDate 2024-04-28 10:24:17
 */
@Service
public class PlanServiceImpl extends ServiceImpl<PlanMapper, Plan>
        implements PlanService {

    @Resource
    private ElasticsearchClient esClient;

    @Override
    public boolean save(PlanAddDTO dto) {
        if (Objects.isNull(dto)) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        String title = dto.getTitle();
        String content = dto.getContent();
        LocalDate doDate = dto.getDoDate();

        // 判断参数合法性
        if (StringUtils.isAnyBlank(title, content)) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        if (Objects.isNull(doDate)) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        // 构造插入对象
        Plan plan = new Plan();
        ContextUser contextUser = UserThreadLocal.get();
        BeanUtils.copyProperties(dto, plan);
        plan.setUserId(contextUser.getId());
        // 默认管理端添加是已认证了
        Integer result = this.getBaseMapper().insert(plan);
        if (null == result || result < 1) {
            throw new BusinessException(ReturnCodeEnum.SAVE_ERROR);
        }
        return true;
    }

    @Override
    public boolean delete(Long id) {
        ThrowUtils.throwIf(Objects.isNull(id) || id <= 0, ReturnCodeEnum.PARAMS_ERROR);
        // 删除课程
        Integer result = this.getBaseMapper().deleteById(id);
        if (null == result || result < 1) {
            throw new BusinessException(ReturnCodeEnum.DELETE_ERROR);
        }
        return true;
    }

    @Override
    public boolean update(PlanDTO dto) {
        ThrowUtils.throwIf(Objects.isNull(dto), ReturnCodeEnum.PARAMS_ERROR);
        Plan dbPlan = this.getBaseMapper().selectById(dto.getId());
        if (Objects.isNull(dbPlan)) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR, "该条计划信息不存在！");
        }
        Plan plan = new Plan();
        BeanUtils.copyProperties(dto, plan);
        // 更课程练信息
        Integer result = this.getBaseMapper().updateById(plan);
        if (null == result && result < 1) {
            throw new BusinessException(ReturnCodeEnum.UPDATE_ERROR, "计划修改失败！");
        }
        return true;
    }

    @Override
    public Plan getPlanById(Long id) {
        Plan plan = this.getBaseMapper().selectById(id);
        return plan;
    }

    @Override
    public boolean changeStatus(Long id, Integer status) {
        // 校验参数id是否合法
        ThrowUtils.throwIf(Objects.isNull(id) || id < 0, ReturnCodeEnum.PARAMS_ERROR);
        // 校验status参数是否合法
        ThrowUtils.throwIf(Objects.isNull(status) ||
                        !PlanConstant.STATUS_LIST.contains(status)
                , ReturnCodeEnum.PARAMS_ERROR);

        Plan dbPlan = this.getBaseMapper().selectById(id);
        ThrowUtils.throwIf(Objects.isNull(dbPlan), ReturnCodeEnum.PARAMS_ERROR);
        dbPlan.setStatus(status);
        Integer result = this.getBaseMapper().updateById(dbPlan);
        if (result == null || result < 1) {
            throw new BusinessException(ReturnCodeEnum.UPDATE_ERROR, "修改计划状态失败！");
        }
        return true;
    }

    @Override
    public List<Plan> getPlanByUserId(Long userId) {
        if (Objects.isNull(userId) || userId < 0) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }
        List<Plan> planList = this.getBaseMapper().selectList(
                new QueryWrapper<Plan>().eq("user_id", userId)
                        .orderByDesc("do_date")
                        .orderByAsc("status"));
        return planList;
    }

    @Override
    public List<Plan> searchFromES(PlanSearchDTO dto) throws IOException {
        String searchText = dto.getSearchText();
        Long userId = dto.getUserId();
        List<Plan> planList;
        if (StringUtils.isBlank(searchText)) {
            planList = this.getPlanByUserId(userId);
            if (CollectionUtil.isEmpty(planList)) {
                throw new BusinessException(ReturnCodeEnum.NOT_FOUND_ERROR, "未找到该用户的计划！");
            }
            return planList;
        }

        // 创建bool查询进行全文模糊匹配
        BoolQuery.Builder boolBuilder = new BoolQuery.Builder();
        BoolQuery boolQuery = boolBuilder.should(s -> s
                        .match(m -> m
                                .field("title")
                                .query(searchText)))
                .should(s -> s
                        .match(m -> m
                                .field("content")
                                .query(searchText))).build();

        // 想要好看一点搞一个高亮显示
        Highlight.Builder highlightBuilder = new Highlight.Builder();
        Highlight highlight = highlightBuilder.fields("title", hf -> hf
                        .preTags("<span style='color:red;font-weight: bold''>")
                        .postTags("</span>"))
                .fields("content", hf -> hf
                        .preTags("<span style='color:red;font-weight: bold''>")
                        .postTags("</span>")).build();

        SearchRequest.Builder builder = new SearchRequest.Builder();
        SearchRequest searchRequest = builder.index("plan").query(q -> q
                        .bool(boolQuery))
                .highlight(highlight)
                .sort(s -> s
                        .field(f -> f.field("status")
                                .order(SortOrder.Asc)))
                .sort(s -> s
                        .field(f -> f.field("doDate")
                                .order(SortOrder.Desc))).build();
        SearchResponse<Plan> response = esClient.search(searchRequest, Plan.class);

        HitsMetadata<Plan> metadata = response.hits();
        if (Objects.nonNull(metadata.total()) && metadata.total().value() == 0L){
            return Collections.emptyList();
        }
        List<Hit<Plan>> hits = metadata.hits();
        planList = hits.stream().map(hit -> {
            Plan source = hit.source();
            Map<String, List<String>> map = hit.highlight();
            if (map.containsKey("title")) {
                source.setTitle(map.get("title").get(0));
            }
            if (map.containsKey("content")) {
                source.setContent(map.get("content").get(0));
            }
            return source;
        }).filter(Objects::nonNull).filter(x->x.getUserId().equals(userId)).collect(Collectors.toList());
        return planList;
    }
}




